﻿Imports System
Imports System.Text

Public Class Ean13Barcode

    Private Const QUITE_ZONE As String = "000000000"
    Private Const LEAD_TAIL As String = "101"
    Private Const SEPARATOR As String = "01010"

    Private _fMinimumAllowableScale As Single = 0.8F
    Private _fMaximumAllowableScale As Single = 2.0F

    ' This is the nominal size recommended by the EAN.
    Private _fWidth As Single = 37.29F
    Private _fHeight As Single = 8.0F ' 25.93F
    Private _fFontSize As Single = 8.0F
    Private _fScale As Single = 1.0F

    ' Left Hand Digits.
    Private _aOddLeft As String() = {"0001101", "0011001", "0010011", "0111101", "0100011", "0110001", _
    "0101111", "0111011", "0110111", "0001011"}

    Private _aEvenLeft As String() = {"0100111", "0110011", "0011011", "0100001", "0011101", "0111001", _
    "0000101", "0010001", "0001001", "0010111"}

    ' Right Hand Digits.
    Private _aRight As String() = {"1110010", "1100110", "1101100", "1000010", "1011100", "1001110", _
    "1010000", "1000100", "1001000", "1110100"}

    Private _sCode As String

    Public Sub New(ByVal code As String)
        _sCode = code
    End Sub

    Public Sub DrawEan13Barcode(ByVal g As System.Drawing.Graphics, ByVal pt As System.Drawing.PointF)
        Dim width As Single = Me.Width * Me.Scale
        Dim height As Single = Me.Height * Me.Scale

        ' EAN13 Barcode should be a total of 113 modules wide.
        Dim lineWidth As Single = width / 113.0F

        ' Save the GraphicsState.
        Dim gs As System.Drawing.Drawing2D.GraphicsState = g.Save()

        ' Set the PageUnit to millimeter because all of our measurements are in millimeter.
        g.PageUnit = System.Drawing.GraphicsUnit.Millimeter

        ' Set the PageScale to 1, so a millimeter will represent a true millimeter.
        g.PageScale = 1

        Dim brush As New System.Drawing.SolidBrush(System.Drawing.Color.Black)

        Dim xPosition As Single = pt.X ' 0

        Dim sbEAN13 As New System.Text.StringBuilder()

        Dim xStart As Single = pt.X
        Dim yStart As Single = pt.Y
        Dim xEnd As Single = 0

        Dim font As New System.Drawing.Font(Constants.Barcodes.FONTNAME_NUMMERS, Me._fFontSize * Me.Scale)

        Dim sLeftPattern As String = ""

        ' Convert the left hand numbers.
        sLeftPattern = ConvertLeftPattern(Code.Substring(0, 7))

        ' Build the UPC Code.
        sbEAN13.AppendFormat("{0}{1}{2}{3}{4}{1}{0}", QUITE_ZONE, LEAD_TAIL, sLeftPattern, SEPARATOR, _
            ConvertToDigitPatterns(Code.Substring(7), Me._aRight))

        Dim sTempUPC As String = sbEAN13.ToString()

        Dim fTextHeight As Single = g.MeasureString(Code, font).Height * 0.8F ' 80% reductie, anders teveel wit

        ' Draw the barcode lines.
        For i As Integer = 0 To sbEAN13.Length - 1
            If sTempUPC.Substring(i, 1) = "1" Then
                If (xStart = pt.X) Then xStart = xPosition
                ' Save room for the UPC number below the bar code.
                If (i > 12 AndAlso i < 55) OrElse (i > 57 AndAlso i < 101) Then
                    ' Draw space for the number
                    g.FillRectangle(brush, xPosition, yStart, lineWidth, height - fTextHeight)
                Else
                    ' Draw a full line.
                    g.FillRectangle(brush, xPosition, yStart, lineWidth, height)
                End If
            End If

            xPosition += lineWidth
            xEnd = xPosition
        Next

        ' Draw the upc numbers below the line.
        xPosition = xStart - g.MeasureString(Code.Substring(0, 1), font).Width
        Dim yPosition As Single = yStart + (height - fTextHeight)

        ' Draw 1st digit of the country code.
        g.DrawString(Code.Substring(0, 1), font, brush, New System.Drawing.PointF(xPosition, yPosition))

        xPosition += (g.MeasureString(Code.Substring(0, 1), font).Width + 43 * lineWidth) - (g.MeasureString(Code.Substring(1, 6), font).Width)

        ' Draw MFG Number.
        g.DrawString(Code.Substring(1, 6), font, brush, New System.Drawing.PointF(xPosition, yPosition))

        xPosition += g.MeasureString(Code.Substring(1, 6), font).Width + (11 * lineWidth)

        ' Draw Product ID.
        g.DrawString(Code.Substring(7), font, brush, New System.Drawing.PointF(xPosition, yPosition))

        ' Restore the GraphicsState.
        g.Restore(gs)
    End Sub

    Private Function ConvertLeftPattern(ByVal sLeft As String) As String
        Select Case sLeft.Substring(0, 1)
            Case "0"
                Return CountryCode0(sLeft.Substring(1))

            Case "1"
                Return CountryCode1(sLeft.Substring(1))

            Case "2"
                Return CountryCode2(sLeft.Substring(1))

            Case "3"
                Return CountryCode3(sLeft.Substring(1))

            Case "4"
                Return CountryCode4(sLeft.Substring(1))

            Case "5"
                Return CountryCode5(sLeft.Substring(1))

            Case "6"
                Return CountryCode6(sLeft.Substring(1))

            Case "7"
                Return CountryCode7(sLeft.Substring(1))

            Case "8"
                Return CountryCode8(sLeft.Substring(1))

            Case "9"
                Return CountryCode9(sLeft.Substring(1))
            Case Else

                Return ""
        End Select
    End Function

    Private Function CountryCode0(ByVal sLeft As String) As String
        ' 0 Odd Odd Odd Odd Odd Odd 
        Return ConvertToDigitPatterns(sLeft, Me._aOddLeft)
    End Function

    Private Function CountryCode1(ByVal sLeft As String) As String
        ' 1 Odd Odd Even Odd Even Even 
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        ' The two lines below could be replaced with this:
        ' sReturn.Append( ConvertToDigitPatterns( sLeft.Substring( 0, 2 ), this._aOddLeft ) );
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aOddLeft))
        ' The two lines below could be replaced with this:
        ' sReturn.Append( ConvertToDigitPatterns( sLeft.Substring( 4, 2 ), this._aEvenLeft ) );
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aEvenLeft))
        Return sReturn.ToString()
    End Function

    Private Function CountryCode2(ByVal sLeft As String) As String
        ' 2 Odd Odd Even Even Odd Even 
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aEvenLeft))
        Return sReturn.ToString()
    End Function

    Private Function CountryCode3(ByVal sLeft As String) As String
        ' 3 Odd Odd Even Even Even Odd 
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aOddLeft))
        Return sReturn.ToString()
    End Function

    Private Function CountryCode4(ByVal sLeft As String) As String
        ' 4 Odd Even Odd Odd Even Even 
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aEvenLeft))
        Return sReturn.ToString()
    End Function

    Private Function CountryCode5(ByVal sLeft As String) As String
        ' 5 Odd Even Even Odd Odd Even 
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aEvenLeft))
        Return sReturn.ToString()
    End Function

    Private Function CountryCode6(ByVal sLeft As String) As String
        ' 6 Odd Even Even Even Odd Odd 
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aOddLeft))
        Return sReturn.ToString()
    End Function

    Private Function CountryCode7(ByVal sLeft As String) As String
        ' 7 Odd Even Odd Even Odd Even
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aEvenLeft))
        Return sReturn.ToString()
    End Function

    Private Function CountryCode8(ByVal sLeft As String) As String
        ' 8 Odd Even Odd Even Even Odd 
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aOddLeft))
        Return sReturn.ToString()
    End Function

    Private Function CountryCode9(ByVal sLeft As String) As String
        ' 9 Odd Even Even Odd Even Odd 
        Dim sReturn As System.Text.StringBuilder = New StringBuilder()
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(0, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(1, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(2, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(3, 1), Me._aOddLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(4, 1), Me._aEvenLeft))
        sReturn.Append(ConvertToDigitPatterns(sLeft.Substring(5, 1), Me._aOddLeft))
        Return sReturn.ToString()
    End Function

    Private Function ConvertToDigitPatterns(ByVal inputNumber As String, ByVal patterns As String()) As String
        Dim sbTemp As System.Text.StringBuilder = New StringBuilder()
        Dim iIndex As Integer = 0
        For i As Integer = 0 To inputNumber.Length - 1
            iIndex = Convert.ToInt32(inputNumber.Substring(i, 1))
            sbTemp.Append(patterns(iIndex))
        Next
        Return sbTemp.ToString()
    End Function

#Region "-- Attributes/Properties --"

    Public ReadOnly Property MinimumAllowableScale() As Single
        Get
            Return _fMinimumAllowableScale
        End Get
    End Property

    Public ReadOnly Property MaximumAllowableScale() As Single
        Get
            Return _fMaximumAllowableScale
        End Get
    End Property

    Public ReadOnly Property Width() As Single
        Get
            Return _fWidth
        End Get
    End Property

    Public ReadOnly Property Height() As Single
        Get
            Return _fHeight
        End Get
    End Property

    Public ReadOnly Property FontSize() As Single
        Get
            Return _fFontSize
        End Get
    End Property

    Public Property Scale() As Single
        Get
            Return _fScale
        End Get

        Set(ByVal value As Single)
            If value < Me._fMinimumAllowableScale OrElse value > Me._fMaximumAllowableScale Then
                Throw New Exception(("Scale value out of allowable range. Value must be between " & Me._fMinimumAllowableScale.ToString() & " and ") + Me._fMaximumAllowableScale.ToString())
            End If
            _fScale = value
        End Set
    End Property

    Public Property Code() As String
        Get
            Return _sCode
        End Get

        Set(ByVal value As String)
            _sCode = value
        End Set
    End Property

#End Region

End Class